/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  +                                                        +
  + Autore: Davide Gugliotta                               +
  + E-mail: dvd.gugliotta@gmail.com                        +
  +                                                        +
  + FILE: ../modules/scanner.c                             +
  +                                                        +
  + Possibilità di due tipi di scan,  syn scan e tcp scan  +
  +                                                        +
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

  #include "../modules/scanner.h" 
  
  // macro per il calcolo random della porta sorgente, srcp = [0, 0x10000 - 1]
  #define cr_src_port(srcp) { srcp = (rand() % 0x10000) == 0 ? (rand() % 0x10000) + 1 : rand() % 0x10000; }

  // mantiene il timeout per I/O non bloccante
  static struct timeval timeout;
 
  // mantiene la risposta alla select
  static char rselect;

  // calcolo della checksum TCP
  static unsigned long int checksum(unsigned short int *, unsigned short int);

  unsigned short int synscan(struct pkt_scan *sc, char iterato){
    
  /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    +                                                                      +
    + Poichè socket = socket(A_INET, SOCK_RAW, IPPROTO_TCP), il kernel     +
    + le indirizza tutti i datagrammi entranti con protocollo di livello   +
    + superiore uguale a 0x06 = 6dec = tcp. Doppiamo quindi filtrare tutti +  
    + i pacchetti alla ricerca di quello in risposta al SYN inviato.       +
    +                                                                      +
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    
    struct iphdr *filtra_ip_header;
    struct tcphdr *filtra_tcp_header;    
        
    unsigned short int porta_sorgente;
    fd_set in_lettura;
    
    // inizializzo il seme per i numeri pseudo casuali
    srand(time(NULL));
    
    // calcolo un valore random per la porta sorgente
    cr_src_port(porta_sorgente);
    
    sc->tcp_p = (struct tcphdr *) &sc->pkt_buffer[TCP_PSEUDO_H];
      
  /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    +                                                                      +
    + Riempio i campi dell'HEADER TCP                                      +
    +                                                                      +
    + source   2 byte porta sorgente in NETWORK BYTE ORDER                 +
    + dest     2 byte porta destinazione in NETWORK BYTE ORDER             +     
    + seq      4 byte numero di sequenza in NETWORK BYTE ORDER             +
    + ack_seq  4 byte numero di riscontro in NETWORK BYTE ORDER            +
    + doff     4 bit spiazzamento dei dati del pacchetto TCP 5*4 = 20 byte +
    + syn      1 bit apertura connessione                                  +
    + rst      1 bit reset connessione                                     +
    + psh      1 bit push dati tcp al livello applicativo                  +
    + urg      1 bit urgenza ?!?                                           +
    + window   2 byte finestra ricezione                                   +
    + check    2 byte checksum, calcolarla con il valore impostato a ZERO  +
    +                                                                      +
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
   
    sc->tcp_p->source = htons(porta_sorgente);
    sc->tcp_p->dest = sc->d_ip.sin_port;
    sc->tcp_p->seq = htonl(1);
    sc->tcp_p->ack_seq = 0;
    sc->tcp_p->doff = 5;
    sc->tcp_p->fin = 0;
    sc->tcp_p->syn = 1;
    sc->tcp_p->rst = 0;
    sc->tcp_p->psh = 0;
    sc->tcp_p->urg = 0;
    sc->tcp_p->ack = 0;
    sc->tcp_p->res1 = 0;
    sc->tcp_p->res2 = 0;
    sc->tcp_p->window = htons(14600);
    sc->tcp_p->check = 0;
    sc->tcp_p->check = checksum((unsigned short int *)sc->pkt_buffer, TCP_PKT_H + TCP_PSEUDO_H);
  
    // invio i pacchetto
    if( sendto(sc->socket, &(sc->pkt_buffer[TCP_PSEUDO_H]), TCP_PKT_H, 0, (struct sockaddr *)&sc->d_ip, sizeof(sc->d_ip)) < 0 ){
      
      fprintf(stderr, "sendto() : %s\n", strerror(errno));
      exit(EXIT_FAILURE);
    
    }
    
  /*++++++++++++++++++++++++++++++++++++++++++++++++++++++
    +                                                    +
    + Filtro i pacchetti.                                +
    + Sono alla ricerca di un pacchetto con ip sorgente, +
    + ip della vittima e porta di destinazione, la porta +
    + sorgente del pacchetto SYN.                        +
    +                                                    +
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++*/  
   
    filtra_ip_header  = (struct iphdr *)  sc->pkt_buffer_r;
    filtra_tcp_header = (struct tcphdr *) &sc->pkt_buffer_r[IP_PKT_H]; 

    FD_ZERO(&in_lettura);
    FD_SET(sc->socket, &in_lettura);
    timeout.tv_sec  = IO_TIMEOUT_SEC;
    timeout.tv_usec = IO_TIMEOUT_USEC;
    
    while(1)
      if((rselect = select(sc->socket + 1, &in_lettura, NULL, NULL, &timeout)) < 0){
        fprintf(stderr, "select() : %s\n", strerror(errno));
        exit(EXIT_FAILURE);
      }
      else if(rselect == 0){
        if(!iterato) return(synscan(sc, 1));  
        return(2);
      }
      else{
        read(sc->socket, sc->pkt_buffer_r, TCP_PKT_R);
        if(filtra_ip_header->saddr == sc->d_ip.sin_addr.s_addr && filtra_tcp_header->dest == sc->tcp_p->source)
          if(filtra_tcp_header->syn == 1 && filtra_tcp_header->ack == 1)
            return(1);
	  else
	    return(0);
      }

  }

  unsigned short int tcpscan(struct pkt_scan *sc){

    int errore_socket;
    int lunghezza;
    fd_set in_scrittura;
    
  /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    +                                                                                    +
    + La socket è impostata su O_NONBLOCK, questo ci permette di non rimanere            +
    + bloccati in attesa che la connect() restituisca il suo timeout di default.         +
    + Sotto queste condizioni la connect() se fallisce, fallisce con uno stato           +
    + di connessione "in progress", infatti il three-way-handshake viene completato      +
    + in maniera asincrona. Utilizziamo la select() per interrogare lo stato in          +
    + scrittura della connessione (modo asinscono di concludere il 3 way handshake)      +
    + impostando un timeout di 1.5 secondi. Se il timeout scade la porta è probabilmente + 
    + filtrata, altrimenti potrebbe essersi stabilita una connessione, oppure potrebbe   +
    + essersi verificato un errore a fronte di una mancata connessione. L'esistenza di   +
    + un errore può essere letto tramite la getsockopt con opzione SO_ERROR.             +
    +                                                                                    +
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    
    if(connect(sc->socket, (struct sockaddr *)&sc->d_ip, sizeof(struct sockaddr)) < 0)
      if(errno == ECONNREFUSED) 
        return(0);
      else 
	if(errno == EINPROGRESS){
          
          FD_ZERO(&in_scrittura);
          FD_SET(sc->socket, &in_scrittura);
          timeout.tv_sec  = IO_TIMEOUT_SEC;
          timeout.tv_usec = IO_TIMEOUT_USEC;
          
          if((rselect = select(sc->socket + 1, NULL, &in_scrittura, NULL, &timeout)) < 0){
            fprintf(stderr, "select() : %s\n", strerror(errno));
            exit(EXIT_FAILURE);
          }
	  else 
            if(rselect == 0) 
              return(2);
	    else{
              if(getsockopt(sc->socket, SOL_SOCKET, SO_ERROR, &errore_socket, (socklen_t *)&lunghezza) < 0){
                fprintf(stderr, "getsockopt() : %s\n", strerror(errno));
                exit(EXIT_FAILURE);
              }
              if(errore_socket) return(0);
              return(1);
            }
        
        }

    // se la connect viene effettuata, la porta è aperta
    return(1);

  }

  unsigned long int checksum(unsigned short int *buffer, unsigned short int buffer_l){
  
    unsigned long int sum;
    
  /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    +                                                         +
    + Debug contenuto se buffer fosse di tipo unsigned char * +  
    +                                                         +
    + struct tcp_pseudo_header *ps;                           +
    + struct tcphdr *tcp;                                     +
    + ps = (struct tcp_pseudo_header *)buffer;                +
    + printf("\nDUMP tcp pseudo header\n");                   +
    + printf("ip_src      NBO   %08X\n",ps->ip_src);          +
    + printf("ip_dst      NBO   %08X\n",ps->ip_dst);          +
    + printf("zero              %02X\n",ps->zero);            +
    + printf("proto             %02X\n",ps->proto);           +
    + printf("tcpht_l     NBO   %04X\n",ps->tcpht_l );        +
    + printf("\nDUMP tcp header\n");                          +
    + tcp = (struct tcphdr *)&buffer[TCP_PSEUDO_H];           +
    + printf("tcp_src     NBO   %04X\n",tcp->source);         +
    + printf("tcp_dst     NBO   %04X\n",tcp->dest);           +
    + printf("tcp_seq     NBO   %08X\n",tcp->seq);            +
    + printf("tcp_ack_seq NBO   %08X\n",tcp->ack_seq);        +
    +                                                         +
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    
    for(sum=0; buffer_l > 0; buffer_l+=-2)
      sum += *(buffer++);

    sum = (sum >> 16) + (sum & 0xFFFF); 
    return(~sum);

  }
